/**
 * @name       switchToggleButton
 * @category   Core
 * 
 * @param {string} selector Valid jQuery selector
 * @param {object} options Valid Options: 
 *                         "switched"   : function, 
 *                         "width"      : string, 
 *                         "effectTime" : number
 * @example 
 * Core.switchToggleButton('#switch', {
 *     "switched": function(){
 *         console.log('switched');
 *     },
 *     "width":"50%",
 *     "effectTime": 2000
 * });
 * 
 * $('#switch').bind('switched',function(){
 *     console.log('switched');
 * });
 * 
 * @author Sósthenes Neto <sosthenes.neto@sigma.com.br>
 */
Core.switchToggleButton = function(selector, options)
{
    try {
        var _validOptions = {
            "switched": "function",
            "effectTime": "number",
            "width": "string"
        };

        var _switchedCallback = null;
        var _applyWidth = null;
        var _effectTime = 350;

        options = options || {};

        if (typeof(options) !== 'object') {
            throw new Core.Exception('Core.switchToggleButton(): options inválido...');
        }

        for (var i in options) {
            var option = options[i];
            if (false === array_key_exists(i, _validOptions)) {
                throw new Core.Exception(sprintf('Core.switchToggleButton(): option "%s" inválido...', i));
            }
            if (typeof(option) !== _validOptions[i]) {
                throw new Core.Exception(sprintf('Core.switchToggleButton(): option "%s" com o conteúdo inválido o \n' +
                        'esperado era "%s" e o informado foi "%s"...', i, _validOptions[i], typeof(option)));
            }

            switch (i) {
                case "switched":
                    _switchedCallback = option;
                    break;
                case "width":
                    _applyWidth = option;
                case "effectTime":
                    _effectTime = option;
                    break;
            }
        }

        var _klass = {
            "V_BOX": "vBox",
            "H_BOX": "hBox",
            "BOX_ITEM": "item",
            "HOVER": "switchToggleButton-hover",
            "ACTIVE": "switchToggleButton-active",
            "DISABLED": "switchToggleButton-disabled",
            "WRAPPER": "switchToggleButton-wrapper",
            "CONTAINER": "switchToggleButton-container",
            "ELEMENT": "switchToggleButton-element",
            "TEXT": "switchToggleButton-text",
            "TEXT_LEFT": "switchToggleButton-textLeft",
            "TEXT_RIGHT": "switchToggleButton-textRight",
            "DRAG_ITEM": "switchToggleButton-dragItem",
            "DROP_AREA": "switchToggleButton-dropArea",
            "DROP_AREA_HOVER": "switchToggleButton-dropAreaHover",
            "DROP_AREA_ACTIVE": "switchToggleButton-dropAreaActive",
            "DROP_LEFT_AREA": "switchToggleButton-dropLeftArea",
            "DROP_RIGHT_AREA": "switchToggleButton-dropRightArea",
            "get": function(klass, withDot) {
                withDot = withDot || false;
                if (true === withDot) {
                    return '.' + this[klass];
                } else {
                    return this[klass];
                }
            }
        };

        function _options(select) {
            return select.find('option');
        }

        function _wrap(select) {
            //criando o encapulador
            var wrapWidth = (false === is_null(_applyWidth)) ? _applyWidth : select.width();
            var wrapper = $('<div />').addClass(_klass.get('WRAPPER'))
                    .width(wrapWidth);

            //caso o select esteja em um vBox ou hBox seta a classe de box-item para definir o tamanho do elemento
            var boxClasses = _klass.get('V_BOX', true) + ',' + _klass.get('H_BOX', true);
            if (select.parents(boxClasses).length > 0) {
                wrapper.addClass(_klass.get('BOX_ITEM'));
            }

            select.wrap(wrapper)
                    .addClass(_klass.get('ELEMENT'));
        }

        function _attr(select, dragItem, i) {
            //pegando o tabindex do select...
            var tabindex = select.attr('tabindex');
            if (tabindex !== undefined) {
                dragItem.attr('tabindex', tabindex);
            }

            //tratamento para o disabled e readonly...
            var disabled = select.attr('disabled');
            if (disabled !== undefined) {
                throw new Core.Exception(sprintf('Core.switchToggleButton(): elemento index (%d) ' +
                        'está com o atributo "disabled".', i));
            }
            var readonly = select.attr('readonly');
            if (readonly !== undefined) {
                throw new Core.Exception(sprintf('Core.switchToggleButton(): elemento index (%d) ' +
                        'está com o atributo "readonly".', i));
            }
        }

        function _data(select) {

            var left = _options(select).eq(0);
            var right = _options(select).eq(1);

            return {
                "left": {
                    "text": right.text(),
                    "selected": left.is(':selected')
                },
                "right": {
                    "text": left.text(),
                    "selected": right.is(':selected')
                }
            };
        }
        
        return $(selector).each(function(i, elem) {
            var select = $(elem);
            if (select.is('select') && _options(select).length === 2) {
                if (select.hasClass(_klass.get('ELEMENT'))) {
                    throw new Core.Exception(sprintf('Core.switchToggleButton(): elemento index (%d) ' +
                            'já foi transformado', i));
                }

                //encapsulando o select...
                _wrap(select);

                //criando os elementos...
                var dropLeftArea = $('<div />').addClass(_klass.get('DROP_AREA'))
                        .addClass(_klass.get('DROP_LEFT_AREA'));
                var dropRightArea = $('<div />').addClass(_klass.get('DROP_AREA'))
                        .addClass(_klass.get('DROP_RIGHT_AREA'));
                var dragItem = $('<a href="javascript:void(0)" />').addClass(_klass.get('DRAG_ITEM')).attr('id',select.attr('id')+'-dragItem');
                var leftText = $('<span />').addClass(_klass.get('TEXT'))
                        .addClass(_klass.get('TEXT_LEFT'))
                        .text(_data(select).left.text);
                var rightText = $('<span />').addClass(_klass.get('TEXT'))
                        .addClass(_klass.get('TEXT_RIGHT'))
                        .text(_data(select).right.text);
                var container = $('<div />').addClass(_klass.get('CONTAINER'));

                dragItem.append(leftText)
                        .append(rightText);

                container.append(dropLeftArea)
                        .append(dropRightArea)
                        .append(dragItem)
                        .appendTo(select.parents(_klass.get('WRAPPER', true)));

                //posição e largura dos textos...
                var areaTotal = container.width() - dragItem.width();
                var leftPosition = areaTotal;
                var leftWidth = areaTotal;
                var rightPosition = dragItem.width();
                var rightWidth = areaTotal;

                leftText.css('width', sprintf('%dpx', leftWidth));
                rightText.css('width', sprintf('%dpx', rightWidth));
                leftText.css('left', sprintf('-%dpx', leftPosition));
                rightText.css('left', sprintf('%dpx', rightPosition));

                //drag & drop...
                dragItem.draggable({
                    "containment": "parent",
                    "scroll": false,
                    "axis": "x",
                    "revert": "invalid",
                    "revertDuration": _effectTime,
                    "start": function() {
                        container.addClass(_klass.get('ACTIVE'));
                    },
                    "stop": function() {
                        dragItem.focus();
                        //                        if (!dragItem.is(':focus')) {
                        //                            container.removeClass( _klass.get('ACTIVE') );
                        //                        }
                    }
                });
                var dropOptions = {
                    "hoverClass": _klass.get('DROP_AREA_HOVER'),
                    "activeClass": _klass.get('DROP_AREA_ACTIVE')
                };
                dropLeftArea.droppable($.extend({"drop": function() {
                        dragItem.trigger('moveToLeft');
                    }}, dropOptions));
                dropRightArea.droppable($.extend({"drop": function() {
                        dragItem.trigger('moveToRight');
                    }}, dropOptions));

                //ajustando os eventos do dragItem...
                dragItem.bind({
                    "keypress": function(event) {
                        switch (true) {
                            case Core.keyCodeListener(Core.keyCode.LEFT, event):
                                dragItem.trigger('moveToLeft');
                                break;
                            case Core.keyCodeListener(Core.keyCode.RIGHT, event):
                                dragItem.trigger('moveToRight');
                                break;
                            case Core.keyCodeListener(Core.keyCode.SPACE, event):
                                dragItem.trigger('switch');
                                break;
                        }
                    },
                    "focus": function() {
                        container.addClass(_klass.get('ACTIVE'));
                    },
                    "blur": function() {
                        container.removeClass(_klass.get('ACTIVE'));
                    },
                    "moveToLeft": function() {
                        Core.console.debug('Core.switchToggleButton(): moveToLeft called');

                        var option = _options(select).eq(0);
                        select.val(option.val());
                        select.trigger('switched');

                        dropLeftArea.droppable('disable');
                        dropRightArea.droppable('enable');
                        dragItem.animate({"left": "0"}, _effectTime / 2);
                    },
                    "moveToRight": function() {
                        Core.console.debug('Core.switchToggleButton(): moveToRight called');

                        var option = _options(select).eq(1);
                        select.val(option.val());
                        select.trigger('switched');

                        dropLeftArea.droppable('enable');
                        dropRightArea.droppable('disable');

                        var left = container.width() - dragItem.width();
                        dragItem.animate({"left": left + 'px'}, _effectTime / 2);
                    },
                    "switch": function() {
                        Core.console.debug('Core.switchToggleButton(): switch called');

                        if (_data(select).right.selected) {
                            dragItem.trigger('moveToLeft');
                        } else if (_data(select).left.selected) {
                            dragItem.trigger('moveToRight');
                        }
                    },
                    "sync": function() {
                        if (_data(select).left.selected) {
                            dragItem.trigger('moveToLeft');
                        } else if (_data(select).right.selected) {
                            dragItem.trigger('moveToRight');
                        }
                    }
                });

                //ajustando os eventos do container...
                container.click(function() {
                    Core.console.debug('Core.switchToggleButton(): click called');

                    dragItem.trigger('switch');
                    dragItem.focus();
                });
                container.hover(
                        function() {
                            container.addClass(_klass.get('HOVER'));
                        },
                        function() {
                            container.removeClass(_klass.get('HOVER'));
                        }
                );

                //ajustando os eventos do select...
                select.bind('change', function() {
                    Core.console.debug('Core.switchToggleButton(): select[onchange] called');

                    dragItem.trigger('sync');
                });
                if (false === is_null(_switchedCallback)) {
                    select.bind('switched', _switchedCallback);
                }

                //ajustando os atributos...
                _attr(select, dragItem, i);

                //removendo a opacidade do select para ele ficar transparente
                select.css('opacity', '0');
                //removendo a posibilidade de entrar no select com o 'tab'
                select.attr('tabindex', '-1');
                //escondendo o select
                select.hide();
                
                //sincronizando...
                dragItem.trigger('sync');

            } else {
                throw new Core.Exception(sprintf('Core.switchToggleButton(): elemento index (%d) não é um <SELECT /> ' +
                        'ou não tem a quantidade de <OPTION /> necessárias (2)', i));
            }
        });

    } catch (e) {
        Core.exceptionHandler(e);
    }
};